package buildings.dwelling;

import buildings.iterators.DwellingIterator;
import buildings.exceptions.FloorIndexOutOfBoundsException;
import buildings.exceptions.SpaceIndexOutOfBoundsException;
import buildings.Space;
import buildings.Floor;
import buildings.Building;
import java.io.Serializable;
import java.util.Iterator;

public class Dwelling implements Building, Serializable, Cloneable {

    private Floor[] floors;

    public Dwelling(int floorCount, int[] flatCounts) {
        if (floorCount < 1 || floorCount != flatCounts.length) {
            throw new FloorIndexOutOfBoundsException();
        }
        int i = 0;
        floors = new DwellingFloor[floorCount];
        for (i = 0; i < floors.length; i++) {
            floors[i] = new DwellingFloor(flatCounts[i]);
        }
    }

    public Dwelling(Floor[] floors) {
        if (floors.length < 1) {
            throw new FloorIndexOutOfBoundsException();
        }
        this.floors = floors;
    }

    public int getFloorCount() {
        return floors.length;
    }

    public int getSpaceCount() {
        int totalCount = 0;
        for (int i = 0; i < floors.length; i++) {
            totalCount += floors[i].getSpaceCount();
        }
        return totalCount;
    }

    public float getTotalSquare() {
        float totalSquare = 0;
        for (int i = 0; i < floors.length; i++) {
            totalSquare += floors[i].getTotalSquare();
        }
        return totalSquare;
    }

    public int getTotalRoomCount() {
        int totalCount = 0;
        for (int i = 0; i < floors.length; i++) {
            totalCount += floors[i].getTotalRoomCount();
        }
        return totalCount;
    }

    public Floor[] getFloors() {
        return floors;
    }

    public Floor getFloorByNumber(int number) {
        Floor buf = null;
        try {
            buf = floors[number];
        }
        catch(ArrayIndexOutOfBoundsException e) {
            throw new FloorIndexOutOfBoundsException();
        }
        return buf;
    }

    public void setFloorByNumber(int number, Floor floor) {
        try {
            floors[number] = floor;
        }
        catch(ArrayIndexOutOfBoundsException e) {
            throw new FloorIndexOutOfBoundsException();
        }
    }

    //Функция превращения номера квартиры в доме
    //в номер этажа [0] и номер квартиры на этаже [1]
    private int[] transformToFloorAndFlat(int number) {
        if(number > getSpaceCount()) {
            throw new SpaceIndexOutOfBoundsException();
        }
        for (int i = 0; i < floors.length; i++) {
            if (number <= floors[i].getSpaceCount() - 1) {
                return new int[]{i, number};
            } else {
                number -= floors[i].getSpaceCount();
            }
        }
        //По идее до этой точки программа не дойдёт
        //Нужна, чтобы не ругался компилятор
        return new int[]{floors.length - 1, number};
    }

    public Space getSpaceByNumber(int number) {
        int[] floorAndFlat = transformToFloorAndFlat(number);
        return floors[floorAndFlat[0]].getSpaceByNumber(floorAndFlat[1]);
    }
    
    public void setSpaceByNumber(int number, Space flat) {
        int[] floorAndFlat = transformToFloorAndFlat(number);
        floors[floorAndFlat[0]].setSpaceByNumber(floorAndFlat[1], flat);
    }

    public void addSpaceByNumber(int number, Space flat) {
        int[] floorAndFlat = transformToFloorAndFlat(number);
        floors[floorAndFlat[0]].addSpaceByNumber(floorAndFlat[1], flat);
    }

    public void deleteSpaceByNumber(int number) {
        int[] floorAndFlat = transformToFloorAndFlat(number);
        floors[floorAndFlat[0]].deleteSpaceByNumber(floorAndFlat[1]);
    }

    //Быстрая сортировка
    private void quickSort(Space[] flats, int low, int high) {
        int i = low;
        int j = high;
        float x = flats[(low + high) / 2].getSquare();
        do {
            while (flats[i].getSquare() > x) {
                ++i;
            }
            while (flats[j].getSquare() < x) {
                --j;
            }
            if (i <= j) {
                Space temp = flats[i];
                flats[i] = flats[j];
                flats[j] = temp;
                i++;
                j--;
            }
        } while (i <= j);
        if (low < j) {
            quickSort(flats, low, j);
        }
        if (high > i) {
            quickSort(flats, i, high);
        }
    }

    public Space getBestSpace() {
        Space bestSpace = null;
        for(int i = 0; i < floors.length; i++) {
            if(bestSpace.getSquare() < floors[i].getBestSpace().getSquare()) {
                bestSpace = floors[i].getBestSpace();
            }
        }
        return bestSpace;
    }
    
    public Space[] getSortedSpaces() {
        Space[] totalFlats = new Flat[getSpaceCount()];
        //По сути многомерный массив квартир становится одномерным
        for (int floor = 0, current = 0; floor < floors.length; floor++) {
            for (int flat = 0; flat < floors[floor].getSpaceCount();
                    flat++, current++) {
                totalFlats[current] = floors[floor].getSpaceByNumber(flat);
            }
        }
        //Сортировка этого массива
        try {
            quickSort(totalFlats, 0, totalFlats.length - 1);
        }
        catch(ArrayIndexOutOfBoundsException e) {
            throw new SpaceIndexOutOfBoundsException();
        }
        finally {
            return totalFlats;
        }
    }
    
    public Iterator iterator() {
        return new Iterator() {
            private int count = 0;

            public Floor next() {
                if(hasNext())
                    return floors[++count];
                return null;
            }

            public boolean hasNext() {
                return getFloorCount() < count;
            }

            public void remove() throws UnsupportedOperationException {
                throw new UnsupportedOperationException();
            }
        };
    }

    public String toString() {
        StringBuffer buf = new StringBuffer(this.getClass().getSimpleName() 
                + " (" + floors.length);
        for(int i = 0; i < floors.length; i++) {
            buf.append(" ");
            buf.append(floors[i]);
        }
        buf.append(")");
        return buf.toString();
    }

    public boolean equals(Object obj) {
        if(!(obj instanceof Dwelling))
            return false;
        boolean isFloorsEquals = true;
        for(int i = 0; i < floors.length; i++) {
            if(!((Dwelling)obj).getFloorByNumber(i).equals(floors[i])) {
                isFloorsEquals = false;
                break;
            }
        }
        return((((Dwelling)obj).getFloorCount() == floors.length)
                && (isFloorsEquals));
    }

    public int hashCode() {
        int hash = floors.length;
        for(int i = 0; i < floors.length; i++) {
            hash |= floors[i].hashCode();
        }
        return hash;
    }

    public Object clone() throws CloneNotSupportedException {
        Object result = super.clone();
        ((Dwelling)result).floors = (Floor[])floors.clone();
        return result;
    }
    
    
}
